package demo5

// 原型模式 Cloneable | PrototypeManager
// 原型模式使对象能复制自身，并且暴露到接口中，使客户端面向接口编程时，不知道接口实际对象的情况下生成新的对象。
// 原型模式配合原型管理器使用，使得客户端在不知道具体类的情况下，
// 通过接口管理器得到新的实例，并且包含部分预设定配置

// --------------------------------场景1-----------------------------------
// 自我复制，好处不用在不知的情况下再去new
type Cloneable interface {
	Clone() Cloneable
}

// 类似注册器，存储对应的对象
type PrototypeManager struct {
	prototypes   map[string]Cloneable
}
// 构造方法
func NewPrototypeManager() *PrototypeManager{
	return &PrototypeManager{
		prototypes:make(map[string]Cloneable),
	}
}

// register
func (p *PrototypeManager) Set(name string,c Cloneable){
	p.prototypes[name] = c
}

// get
func (p *PrototypeManager)Get(name string) Cloneable {
	return p.prototypes[name]
}

// --------------------------------场景2-----------------------------------
// 原型设计模式，该模式主要有manager对实例对象的注册和发现，简单的说就是管理，外部想要实现，必须要遵守内部的实现方法，否则无法调用
// 内部的实现要完成Clone方法的调用,该模式的使用场景: 频繁的生成对象的地方，资源消耗较大的时候，这里不是太好形容，可以百度了解一些
// 缺点的话，刚刚上句话也基本上概况了，要求较为严格必须实现内部方法，
// 这里使用该模式完成一个场景，该场景也可以使用其他的设计模式，比如说单利等
// 需求，根据不同的table来获取table内容，
// DDD分析设计: 不同table ==> 可能会出现多次的初始化， table 内容可以是根据场景需求来展示内容，这里内容格式使用的string 当然可以使用json原理差不多
// Table | TableContent 内容  table 就是模型亦是对象， 管理table实例的话需要一个TableManger
// 开始
type Table interface{
	Clone() Table // 这个需要实现 这点比较约束
	// 如果有其他的方法同样外部需要实现
}

type TableManager struct{
	tables map[string]Table
}

// 构造初始化
func NewTableManager() *TableManager{
	return &TableManager{
		tables:make(map[string]Table),
	}
}
// 提示: 这里使用抽象接口作为返回值的好处，尽量让实例解耦| 关于设计模式就是尽量保证业务解耦，无状态，

// 注册
func (t *TableManager)Register(name string, table Table){
	t.tables[name] = table
}

// 获取
func (t *TableManager)Obtain(name string) Table{
	return t.tables[name]
}

// 上述表对象可以是不同DB的表 ， 而且这个设计模式对内的基本上变动较少，主要外部的要求变化较大，
// 而且外部的作用范围也是比较大，所以管理中心最好是当前范围全局可以使用

// --------------------------------场景3-----------------------------------
// 通过上面的方法和场景2 看到该设计模式有点类似工厂模式，不同的地方就是初始化的地方不同
// 需求： 支付方式选择,
// 需求分析： 并发下单的时候，支付方式会被多次的选择，那么可以将支付方式实例对象封装起来，
// 该设计模式的封装相对于，比如说策略设计，工厂设计等 更高一层，用户是能够拿到该实例对象就可以使用了，
// 更多的业务逻辑都在下一层的实现了
// 提示: 有些案例会在多个设计模式场景使用，这里还是根据项目需求来的，并非直接去强制使用的，最好的设计模式时根据业务发展出来的
// 建议：可以慢慢尝试自己练习
// 支付 Payment   支付选择PaymenChoice 支付Add  支付Had

type Payment interface{
	Clone() Payment
}

type PaymentChoice struct{
	payments map[string]Payment
}

func NewPaymentChoice() *PaymentChoice{
	return &PaymentChoice{
		payments:make(map[string]Payment),
	}
}
func (p *PaymentChoice) Add(name string, payment Payment){
	p.payments[name] = payment
}
func (p *PaymentChoice)Had(name string) Payment{
	return p.payments[name]
}

